/* 
 * Autores:
 *      Jonathan Lopez  11106
 *      Jorge Martínez  11237
 * 
 * Ultima revision: $Rev$
 *      Fecha: $Date$
 * 
 */

package Hibernate;

import java.sql.Types;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.Hibernate;

/**
 * Clase que funciona como el dialecto SQL que implementa una base de datos 
 * particular, asegurando la compatibilidad con Hibernate.
 * 
 * @author Jorge
 * @author Jonathan
 */
public class SQLiteDialect extends Dialect 
{
    public SQLiteDialect() {
		super();
		registerColumnType(Types.BIT, "integer");
		registerColumnType(Types.TINYINT, "tinyint");
		registerColumnType(Types.SMALLINT, "smallint");
		registerColumnType(Types.INTEGER, "integer");
		registerColumnType(Types.BIGINT, "bigint");
		registerColumnType(Types.FLOAT, "float");
		registerColumnType(Types.REAL, "real");
		registerColumnType(Types.DOUBLE, "double");
		registerColumnType(Types.NUMERIC, "numeric");
		registerColumnType(Types.DECIMAL, "decimal");
		registerColumnType(Types.CHAR, "char");
		registerColumnType(Types.VARCHAR, "varchar");
		registerColumnType(Types.LONGVARCHAR, "longvarchar");
		registerColumnType(Types.DATE, "date");
		registerColumnType(Types.TIME, "time");
		registerColumnType(Types.TIMESTAMP, "timestamp");
		registerColumnType(Types.BINARY, "blob");
		registerColumnType(Types.VARBINARY, "blob");
		registerColumnType(Types.LONGVARBINARY, "blob");
		// registerColumnType(Types.NULL, "null");
		registerColumnType(Types.BLOB, "blob");
		registerColumnType(Types.CLOB, "clob");
		registerColumnType(Types.BOOLEAN, "integer");

		registerFunction("concat", new VarArgsSQLFunction(Hibernate.STRING, "",
				"||", ""));
		registerFunction("mod", new SQLFunctionTemplate(Hibernate.INTEGER,
				"?1 % ?2"));
		registerFunction("substr", new StandardSQLFunction("substr",
				Hibernate.STRING));
		registerFunction("substring", new StandardSQLFunction("substr",
				Hibernate.STRING));
	}
    
    /**
     * 
     * @return retorna true como está especificado en NHibernate dialect.
     */
    public boolean supportsIdentityColumns() 
    {
	return true;
    }
    
    /**
     * 
     * @return retorna true como lo especifica NHibernate dialect.
     */
    public boolean supportsInsertSelectIdentity() 
    {
	return true; 
    }
    
    /**
     * Indica si el "dialecto" tiene una cláusula Identity agregada al tipo de
     * dato o un tipo de dato completamente separado.
     * @return Retorna falso como lo establece el dialecto NHibernate.
     */
    public boolean hasDataTypeInIdentityColumn() 
    {
	return false; 
    }

    /**
     * Primero se emplea supportsInsertSelectIdentity (), después se agrega la
     * instrucción SELECT IDENTITY para insertar la instrucción.
     * @param insertString
     * @return 
     */
    public String appendIdentitySelectToInsert(String insertString) 
    {
	 return new StringBuffer(insertString.length()+30). 
	 append(insertString).
	 append("; ").append(getIdentitySelectString()).
	 toString();
    }

    /**
     * La sintaxis utilizada en DDL para definir una columna como IDENTITY.
     * @return Una cadena "integer".
     */
    public String getIdentityColumnString() 
    {
        return "integer";
    }

    /**
     * Recupera el último valor IDENTITY generado.
     * @return valor IDENTITY 
     */
    public String getIdentitySelectString() 
    {
	return "select last_insert_rowid()";
    }

    /**
     * Se pregunta si la sentencia tiene alguna forma de limitar los resultados
     * de las consultas a través de una cláusula SQL.
     * @return 
     */
    public boolean supportsLimit() 
    {
	return true;
    }

    /**
     * Aplica un límite a un query
     * @param query
     * @param hasOffset
     * @return el SQL modificado
     */
    public String getLimitString(String query, boolean hasOffset) 
    {
	return new StringBuffer(query.length() + 20).append(query).append(
        	hasOffset ? " limit ? offset ?" : " limit ?").toString();
    }

    /**
     * Se pregunta si una tabla temporanea puede ser soportada
     * @return 
     */
        public boolean supportsTemporaryTables() 
    {
	return true;
    }

    /**
     * Crea una tabla temporanea.
     * @return 
     */
    public String getCreateTemporaryTableString() 
    {
        return "create temporary table if not exists";
    }

    /**
     * Se pregunta si es necesario deshechar la tabla temporanea después de 
     * usada.
     * @return 
     */
    public boolean dropTemporaryTableAfterUse() 
    {
	return false;
    }

    /**
     * Se pregunta si se puede recuperar la fecha y hora actual de la base de 
     * datos.
     * @return 
     */
    public boolean supportsCurrentTimestampSelection() 
    {
	return true;
    }

    /**
     * Si el valor devuelto por el método puede tratarse de la forma en que se 
     * llamó; típicamente indica que una sintaxis de escape de JDBC está siendo 
     * usada.
     * @return verdadero si puede ser tratado como se llama, falso de otra forma.
     */
    public boolean isCurrentTimestampSelectStringCallable() 
    {
	return false;
    }

    /**
     * Recupera el comando que se utiliza para recuperar el timestammp actual de
     * la base de datos.
     * @return 
     */
    public String getCurrentTimestampSelectString() 
    {
	return "select current_timestamp";
    }

    /**
     * Se pregunta si ese "dialecto" soporta la instrucción UNION ALL que es 
     * generalmente más rápida que la instrucción UNION.
     * @return 
     */
    public boolean supportsUnionAll() 
    {
	return true;
    }

    /**
     * Se pregunta si es necesario quitar las restricciones antes de dejar las
     * tablas en ese "dialecto".
     * @return 
     */
    public boolean hasAlterTable() 
    {
	return false; 
    }

    /**
     * 
     * @return 
     */
    public boolean dropConstraints() 
    {
	return false;
    }

    /**
     * La sintaxis utilizada para añadir una columna a una tabla (opcional).
     * @return El fragmento de la columna agregada.
     */
    public String getAddColumnString() 
    {
	return "add column";
    }

    /**
     * Obtiene la cadena que se añadirá a la instrucción SELECT para adquirir
     * bloqueos para ese "dialecto".
     * @return 
     */
    public String getForUpdateString() 
    {
	return "";
    }

    /**
     * Se pregunta si el "dialecto" soporta la instrucción FOR UPDATE en conjunto
     * con filas externas adjuntas.
     * @return 
     */
    public boolean supportsOuterJoinForUpdate() 
    {
	return false;
    }

    /**
     * 
     * @return 
     */
    public String getDropForeignKeyString() 
    {
	throw new UnsupportedOperationException(
        	"No drop foreign key syntax supported by SQLiteDialect");
    }

    /**
     * Obtiene la sintaxis utilizada para la llave foranea de una tabla.
     * @param constraintName
     * @param foreignKey
     * @param referencedTable
     * @param primaryKey
     * @param referencesPrimaryKey
     * @return el fragmento de la llave foranea agregada.
     */
    public String getAddForeignKeyConstraintString(String constraintName,
		String[] foreignKey, String referencedTable, String[] primaryKey,
		boolean referencesPrimaryKey) 
    {
	throw new UnsupportedOperationException(
		"No add foreign key syntax supported by SQLiteDialect");
    }

    /**
     * Obtiene la sintaxis utilizada para la llave primaria de una tabla.
     * @param constraintName
     * @return el fragmento de la llave primaria agregada.
     */
    public String getAddPrimaryKeyConstraintString(String constraintName) 
    {
	throw new UnsupportedOperationException(
		"No add primary key syntax supported by SQLiteDialect");
    }

    /**
     * 
     * @return 
     */
    public boolean supportsIfExistsBeforeTableName() 
    {
	return true;
    }

    /**
     * 
     * @return 
     */
    public boolean supportsCascadeDelete() 
    {
	return false;
    }
}
